#!/usr/bin/env python3
#Copyright 2012 Daniel C.(llaniscudani@gmail.com)

#This file is part of Radius3000.

#Radius3000 is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.

#Radius3000 is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with Radius3000.  If not, see <http://www.gnu.org/licenses/>.

#server.py is the main file of the server and work with the libs.
#This server is oriented to authenticate Wi-Fi users with
#RADIUS -> EAP -> PEAPv0 -> MS-CHAP-V2 and check users in SQL DB

#TEST VERSION. ALPHA
__version__ = "0.1"

import logging
import sys
import socketserver
import sqlite3
import radius
import eap
import _thread
import socket
import ssl
import mschapv2



#logging options
logging.basicConfig(format='%(asctime)-15s[%(levelname)s] %(message)s',level=6)


ADDR=""
PORT=1812

TLS_KEY = sys.path[0] + "/cert.pem"
TLS_CERT = sys.path[0] + "/cert.pem"

logging.debug("Starting RADIUS SERVER...")

#open db
db = sqlite3.connect(sys.path[0] + "/db.sqlite")


class IEEE802_1_Server(radius.RADIUSRequestHandler):        
    
    def check_db(self, user):
        #check the user is in db
        c = db.cursor()
        c.execute('SELECT user FROM users WHERE user = "{0}"'.format(user))
        if len(c.fetchone()) == 1:
            c.close()
        else:
            c.close()
            return


    def tls_server(self):
        #STEP 2
        #tls server
        sock = ssl.wrap_socket(self.tls_srv,
            keyfile = TLS_KEY,
            certfile = TLS_CERT,
            server_side = True,
            cert_reqs = ssl.CERT_NONE,
            ssl_version = ssl.PROTOCOL_TLSv1)
        
        e = eap.EAPRequestHandler()
        c = mschapv2.MSCHAPv2()
                
        #send eap request identity
        sock.send(e.request_identity())
        #check identity...
        data = sock.recv(1024)
        if data[0:1] == b'\x01':
            #identity
            logging.debug(data)

        
        #send mschapv2 challenge
        sock.send(c.challenge())
        #check if response is identification or nak
        data = sock.recv(1024)
        
        #if all ok send sucess        
        sock.send(c.parse(data))

        #recv sucess
        data = sock.recv(1024)
        data = c.parse(data)
        
        #send eap-tlv
        sock.send(e.eap_tlv_result_avp())
        #recv tlv
        data = sock.recv(1024)
        logging.debug(data)       
        
        #phase2 end
        self.tls_phase2 = False
                
        #make session keys
        self.session_keys = c.sessionKeysGenerator('aaaa')

        sock.close()
        _thread.exit()
        

    def access_request(self):
        #input socket
        sock = self.request[1]
        
        self.shared_secret = b'aaaa'
        #when receive a access request from user...
        self.check_db(self.attributes['User-Name'])
        
        if 'EAP-Message' in self.attributes:
            #if a eap message in access request
            #server works as relay with eap
            e = eap.EAPRequestHandler()
            e.parse(self.attributes['EAP-Message'])
            #self.client_address[0]
            #EAP code:2 = response and type:1 = identify
            if e.code == 2 and e.type == 1:
                #response with radius challenge eap start challenge packet
                sock.sendto(self._accessChallenge(self._eapMessage(e.peap_start())), self.client_address)
                #parse incoming packet
                self.parse_request(sock.recv(1024))
                
                if 'EAP-Message' in self.attributes:
                    #parse the new eap packet
                    e.parse(self.attributes['EAP-Message'])
                    #EAP type:25 = PEAP 
                    if e.code == 2 and e.type == 25:
                        #open tls sockets
                        self.tls_srv, self.tls_cli = socket.socketpair()
                        #thread tls server
                        _thread.start_new_thread(self.tls_server, ())
                        
                        #####
                        #    tls handshake
                        #####
                        
                        ##
                        #step 1
                        #in this point the user send a TLS client hello.
                        ##
                        
                        ##test##
                        #from tls packet extract the client_hello random for PRF
                        if e.data[5:6] == b'\x16' and e.data[10:11] == b'\x01':
                            client_hello_random = e.data[20:48]                        
                        
                        #send the client hello PEAP handshake to tls server
                        self.tls_cli.send(e.peap_parse(e.data))
                        
                        #receive from server "server hello"
                        re = self.tls_cli.recv(1024)
                        
                        ##test##
                        #extract the server hello random
                        if re[0:1] == b'\x16' and re[5:6] == b'\x02':
                            server_hello_random = re[15:43]
                        
                        #send the response of tls server to user
                        sock.sendto(self._accessChallenge(self._eapMessage(
                                    e.peap_packet(re))), self.client_address)

                                                
                        #TLS HANDSHAKE
                        
                        #TLS bridge User <-> TLS server
                        #main server passthrough to phase2 server
                        self.tls_phase2 = True
                        
                        while self.tls_phase2:
                            #user -to-> radius -to-> server tls
                            #parse new incoming radius paquet
                            self.parse_request(sock.recv(1024))
                            #parse the new eap packet extract tls in peap
                            #and send to phase 2 server                   
                            self.tls_cli.send(e.peap_parse(e.parse(self.attributes['EAP-Message'])))
                            
                            ru = self.tls_cli.recv(1024)
                            if self.tls_phase2 == True:
                                #server tls -to-> user
                                sock.sendto(self._accessChallenge(self._eapMessage(
                                        e.peap_packet(ru))), self.client_address)
                                    
                        logging.debug('phase 2 end')
                        
                        #radius attributes
                        #eap sucess
                        attr = self._eapMessage(e.sucess()) + self._ms_mppe_keys()
                        #Last Step is send the session keys to AP
                        #MS-MPPE-Recv-Key
                        #MS-MPPE-Send-Key
                        print('0')
                        
                        #making PRF
                        random = client_hello_random + server_hello_random

                        
                        sock.sendto(self._accessAccept(attr), self.client_address)

                        

            
            


Handler = IEEE802_1_Server
radiusd = socketserver.UDPServer((ADDR, PORT), Handler)
try:
    radiusd.serve_forever()
except KeyboardInterrupt:
    print('\nClosed')
